﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Xml;
using System.Xml.Linq;
using System.Collections;
using System.Data;
using SPWSWrapper.WSCopy;
using System.IO;
using SPWSWrapper.WSDspSts;
using SPWSWrapper.WSLists;
using SPWSWrapper.WSSiteData;
using SPWSWrapper.WSWebs;
using System.Drawing;
using System.Xml.XPath;
using Microsoft.SharePoint.Client;
using ClientNS = Microsoft.SharePoint.Client;
using System.Reflection;
using System.Diagnostics;
using SPWSWrapper.WSUserGroup;

namespace SPWSWrapper
{
    /// <summary>
    /// Used to provide a wrapper for SharePoint 2007 
    /// Web Services with limited SP2010 support
    /// </summary>
    public class WebSP
    {
        #region Private Members
        private decimal _UIVersion = 3.0m;
        private string _UserName = "";
        private string _Password = "";
        private string _Domain = "";
        private string _WebUrl = "";
        private bool _WSReady = false;
        private bool _UseCurrentCredentials = false;
        private Lists _ListsWS;
        private StsAdapter _ListDataWS;
        private Copy _CopyWS;
        private SiteData _SiteDataWS;
        private Webs _WebsWS;
        private UserGroup _UserGroup;
        private bool CredentialsSupplied = false;
        private ClientContext _Ctx;
        #endregion
        #region Public Members
        /// <summary>
        /// Set true if you want to use the credentials of the user running
        /// this program to run the SharePoint Web Service calls
        /// </summary>
        public bool UseCurrentCredentials
        {
            get { return _UseCurrentCredentials; }
            set
            {
                if (value != _UseCurrentCredentials)
                {
                    AreAllCredentialsSupplied(true);
                    _UseCurrentCredentials = value;
                }
                else
                {
                    _UseCurrentCredentials = value;
                    CredentialsSupplied = true;
                    Init_WS();
                    
                }
            }
        }
        /// <summary>
        /// User Name used for the Web Service calls
        /// </summary>
        public string UserName { get { return _UserName; } set { _UserName = value; AreAllCredentialsSupplied(true); } }
        /// <summary>
        /// Password used for the Web Service calls
        /// </summary>
        public string Password { get { return _Password; } set { _Password = value; AreAllCredentialsSupplied(true); } }
        /// <summary>
        /// Domain used for the Web Service calls user authentication
        /// </summary>
        public string Domain { get { return _Domain; } set { _Domain = value; AreAllCredentialsSupplied(true); } }
        /// <summary>
        /// Url of the SharePoint Site that hosts the Web Services
        /// </summary>
        public string WebUrl { get { return _WebUrl; } set { _WebUrl = value; AreAllCredentialsSupplied(true); } }
        /// <summary>
        /// Read Only property used to determine the SharePoint version connected to. MOSS 2007 = 3.0 and SP2010 = 4.0
        /// </summary>
        public decimal UIVersion { 
            get { return _UIVersion; }
            set
            {
                if (_UIVersion != value)
                {
                    //ReInitialize
                    if (_UIVersion >= 4.0m)
                        Init_WS2010();
                    else
                        Init_WS();
                }
                _UIVersion = value;
            }
        }

        #endregion

        #region Constructors
        public WebSP()
        {
            _UseCurrentCredentials = true;
            AreAllCredentialsSupplied(true);
        }
        /// <summary>
        /// Used to initialize the settings used in web service calls.
        /// Using this constructor the class will set UseCurrentCredentials to true
        /// </summary>
        /// <param name="webUrl">Url of the SharePoint Site that hosts the web services</param>
        public WebSP(string webUrl)
        {
            _WebUrl = webUrl;
            _UseCurrentCredentials = true;
            AreAllCredentialsSupplied(true);
        }
        /// <summary>
        /// Used to initialize the credentials used in web service calls
        /// </summary>
        /// <param name="userName">User Name used for the Web Service calls</param>
        /// <param name="password">Password used for the Web Service calls</param>
        /// <param name="domain">Domain used for the Web Service calls user authentication</param>
        /// <param name="webUrl">Url of the SharePoint Site that hosts the Web Services</param>
        public WebSP(string userName, string password, string domain, string webUrl)
        {
            _UserName = userName;
            _Password = password;
            _Domain = domain;
            _WebUrl = webUrl;
            UseCurrentCredentials = false;
            AreAllCredentialsSupplied(true);
        }
        #endregion
        #region Initializers
        private void ReInitializeAll()
        {
            Init_WS();
        }
        private void Init_WS2010()
        {
            //Check if credentials are supplied
            if (!CredentialsSupplied)
                throw new Exception("Cannot call web services without credentials! " +
                                    "Please use SetCredentials() or set UserCurrentCredentials = true!");
            //WebService Settings
            _ListsWS = new Lists();
            _ListDataWS = new StsAdapter();
            _CopyWS = new Copy();
            _SiteDataWS = new SiteData();
            _WebsWS = new Webs();
            _UserGroup = new UserGroup();

            //SharePoint 2010 Ctx
            _Ctx = new ClientContext(_WebUrl);

            _ListsWS.Url = _WebUrl + "/_vti_bin/Lists.asmx";
            _ListDataWS.Url = _WebUrl + "/_vti_bin/DspSts.asmx";
            _CopyWS.Url = _WebUrl + "/_vti_bin/Copy.asmx";
            _SiteDataWS.Url = _WebUrl + "/_vti_bin/SiteData.asmx";
            _WebsWS.Url = _WebUrl + "/_vti_bin/Webs.asmx";
            _UserGroup.Url = _WebUrl + "/_vti_bin/UserGroup.asmx";

            //Special settings
            string[] vArray = new string[1] { "1.0" };
            WSDspSts.Versions myVersion = new WSDspSts.Versions();
            myVersion.version = vArray;
            _ListDataWS.versions = myVersion;

            WSDspSts.RequestHeader reqHeader = new WSDspSts.RequestHeader();
            reqHeader.document = WSDspSts.DocumentType.content;
            reqHeader.method = WSDspSts.MethodType.query;

            _ListDataWS.request = reqHeader;

            if (UseCurrentCredentials)
            {
                _ListsWS.UseDefaultCredentials = true;
                _ListDataWS.UseDefaultCredentials = true;
                _CopyWS.UseDefaultCredentials = true;
                _SiteDataWS.UseDefaultCredentials = true;
                _WebsWS.UseDefaultCredentials = true;
                _UserGroup.UseDefaultCredentials = true;
                _Ctx.Credentials = System.Net.CredentialCache.DefaultCredentials;
            }
            else
            {
                _ListsWS.UseDefaultCredentials = false;
                NetworkCredential Cred = new NetworkCredential(_UserName, _Password, _Domain);
                _ListsWS.Credentials = Cred;
                _ListDataWS.Credentials = Cred;
                _CopyWS.Credentials = Cred;
                _SiteDataWS.Credentials = Cred;
                _WebsWS.Credentials = Cred;
                _UserGroup.Credentials = Cred;

                _Ctx.Credentials = Cred;

            }
            _WSReady = true;
        }
        private void Init_WS()
        {
            //Check if credentials are supplied
            if (!CredentialsSupplied)
                throw new Exception("Cannot call web services without credentials! " +
                                    "Please use SetCredentials() or set UserCurrentCredentials = true!");
            //WebService Settings
            _ListsWS = new Lists();
            _ListDataWS = new StsAdapter();
            _CopyWS = new Copy();
            _SiteDataWS = new SiteData();
            _WebsWS = new Webs();
            _UserGroup = new UserGroup();

            _ListsWS.Url = _WebUrl + "/_vti_bin/Lists.asmx";
            _ListDataWS.Url = _WebUrl + "/_vti_bin/DspSts.asmx";
            _CopyWS.Url = _WebUrl + "/_vti_bin/Copy.asmx";
            _SiteDataWS.Url = _WebUrl + "/_vti_bin/SiteData.asmx";
            _WebsWS.Url = _WebUrl + "/_vti_bin/Webs.asmx";
            _UserGroup.Url = _WebUrl + "/_vti_bin/UserGroup.asmx";


            //Special settings
            string[] vArray = new string[1] { "1.0" };
            WSDspSts.Versions myVersion = new WSDspSts.Versions();
            myVersion.version = vArray;
            _ListDataWS.versions = myVersion;

            WSDspSts.RequestHeader reqHeader = new WSDspSts.RequestHeader();
            reqHeader.document = WSDspSts.DocumentType.content;
            reqHeader.method = WSDspSts.MethodType.query;

            _ListDataWS.request = reqHeader;

            if (UseCurrentCredentials)
            {
                _ListsWS.UseDefaultCredentials = true;
                _ListDataWS.UseDefaultCredentials = true;
                _CopyWS.UseDefaultCredentials = true;
                _SiteDataWS.UseDefaultCredentials = true;
                _WebsWS.UseDefaultCredentials = true;
                _UserGroup.UseDefaultCredentials = true;
            }
            else
            {
                _ListsWS.UseDefaultCredentials = false;
                NetworkCredential Cred = new NetworkCredential(_UserName, _Password, _Domain);
                _ListsWS.Credentials = Cred;
                _ListDataWS.Credentials = Cred;
                _CopyWS.Credentials = Cred;
                _SiteDataWS.Credentials = Cred;
                _WebsWS.Credentials = Cred;
                _UserGroup.Credentials = Cred;


            }
            _WSReady = true;
        }
        
        /// <summary>
        /// Used to initialize the credentials of used in the Web Service calls
        /// </summary>
        /// <param name="userName">User Name used for the Web Service calls</param>
        /// <param name="password">Password used for the Web Service calls</param>
        /// <param name="domain">Domain used for the Web Service calls user authentication</param>
        /// <param name="webUrl">Url of the SharePoint Site that hosts the Web Services</param>
        public void Initialize(string userName, string password, string domain, string webUrl)
        {
            _UserName = userName;
            _Password = password;
            _Domain = domain;
            _WebUrl = webUrl;
            _UseCurrentCredentials = false;
            AreAllCredentialsSupplied(true);
        }
        /// <summary>
        /// Used to initialize the credentials of used in the Web Service calls
        /// </summary>
        /// <param name="webUrl">Url of the SharePoint Site that hosts the Web Services</param>
        public void Initialize(string webUrl)
        {
            _WebUrl = webUrl;
            UseCurrentCredentials = true;
        }
        public bool AreAllCredentialsSupplied(bool ReInitialize)
        {
            CredentialsSupplied = ((!string.IsNullOrEmpty(_UserName) &&
                                    !string.IsNullOrEmpty(_Password) &&
                                    !string.IsNullOrEmpty(_Domain)) ||
                                   _UseCurrentCredentials) &&
                                  !string.IsNullOrEmpty(_WebUrl);
            if (CredentialsSupplied && ReInitialize)
                ReInitializeAll();
            return CredentialsSupplied;
        }
        #endregion
        #region Auxilary Methods
        private string GetUserID(XmlNode UserNode)
        {
            if (UserNode != null && UserNode.Attributes.Count > 0)
                return UserNode.Attributes[0].Value;
            return string.Empty;
        }
        private DataTable XmlToDataTable(XmlNode XmlData, string TableName)
        {
            DataTable Result = new DataTable(TableName);
            //Create table columns
            foreach (XmlAttribute Att in XmlData.ChildNodes[0].Attributes)
                Result.Columns.Add(Att.Name);

            foreach (XmlNode item in XmlData.ChildNodes)
            {

                DataRow newDR = Result.NewRow();
                foreach (XmlAttribute Att in item.Attributes)
                {
                    if(Result.Columns.Contains(Att.Name))
                    newDR[Att.Name] = Att.Value;
                }
                Result.Rows.Add(newDR);
            }
            //Remove column prefix
            foreach (DataColumn item in Result.Columns)
                item.Caption = item.ColumnName = item.Caption.Replace("ows_", "").Replace("_x0020_", " ");

            //Add control columns
            DataColumn FilterDataColumn = new DataColumn("System:ItemsFilter");
            Result.Columns.Add(FilterDataColumn);

            return Result;
        }
        private DataTable XmlToDataTable2010(XmlNode XmlData, string TableName)
        {
            bool FileTypeColumnAdded = false;
            DataTable Result = new DataTable(TableName);
            //Add column for icon
            Result.Columns.Add("  ", typeof(Image));

            //Create table columns
            if (XmlData.ChildNodes == null || XmlData.ChildNodes.Count < 2) return Result;
            foreach (XmlAttribute Att in XmlData.ChildNodes[1].Attributes)
                Result.Columns.Add(Att.Name);

           //Add a new column for the file types
            if (Result.Columns.Contains("ows_BaseName") &&
                Result.Columns.Contains("ows_FileLeafRef") &&
                !Result.Columns.Contains("ows_File_x0020_Type"))
            {
                Result.Columns.Add("File Type");
                FileTypeColumnAdded = true;
            }

            foreach (XmlNode item in XmlData.ChildNodes)
            {
                if (item.Name == "#whitespace") continue;
                DataRow newDR = Result.NewRow();

                //Get the ID attribute
                string ID = "";
                XmlAttribute IDatt = item.Attributes["ows_ID"];
                if (IDatt != null)
                    ID = IDatt.Value +";#";
                foreach (XmlAttribute Att in item.Attributes)
                {
                    if (Result.Columns.Contains(Att.Name))
                    {
                        string Val = System.Web.HttpUtility.HtmlDecode(Att.Value);
                        //Remove ID prefix
                        Val = Val.Replace(ID, "");
                        newDR[Att.Name] = Val;
                    }
                }
                //Add the file type value
                if (FileTypeColumnAdded)
                {
                    string FileType = newDR["ows_FileLeafRef"].ToString().ToLower().Replace(
                                         newDR["ows_BaseName"].ToString().ToLower() + ".", "");
                    //This happens if the item is a folder
                    if (FileType == newDR["ows_BaseName"].ToString().ToLower())
                        FileType = newDR["ows_ContentType"].ToString();
                        newDR["File Type"]=FileType;
                }

                Result.Rows.Add(newDR);
            }
            
            //Remove column prefix
            foreach (DataColumn item in Result.Columns)
                item.Caption = item.ColumnName = item.Caption.Replace("ows_", "").Replace("_x0020_"," ");
            



            return Result;
        }
        private string GetFieldString(object FieldData)
        {
            String FieldValue = "";
            Type FieldType = null;
            if (FieldData == null) return null;
            FieldType = FieldData.GetType();
            if (FieldType == typeof(ClientNS.FieldLookupValue))
            {
                ClientNS.FieldLookupValue LField = FieldData as ClientNS.FieldLookupValue;
                if (LField != null && LField.LookupValue != null)
                    FieldValue = LField.LookupValue.ToString();
            }
            else if (FieldType == typeof(ClientNS.FieldUserValue))
            {
                ClientNS.FieldUserValue UField = FieldData as ClientNS.FieldUserValue;
                if (UField != null && UField.LookupValue != null)
                    FieldValue = UField.LookupValue.ToString();
            }
            else
                FieldValue = FieldData.ToString();
            return FieldValue;
        }
        private DataTable XmlToDataTable2010(List CurrentList, ListItemCollection Data)
        {

            
            DataTable Result = new DataTable("");
            if (!string.IsNullOrEmpty(CurrentList.Title))
                Result.TableName = CurrentList.Title;

            //Add column for icon
            Result.Columns.Add("  ", typeof(Image));

            //Create table columns
            if (CurrentList.Fields == null || CurrentList.Fields.Count <= 0) return Result;
            foreach (ClientNS.Field Att in CurrentList.Fields)
                Result.Columns.Add(Att.InternalName);



            foreach (ClientNS.ListItem item in Data)
            {
                DataRow newDR = Result.NewRow();

                foreach (var Att in item.FieldValues)
                {
                    if (Result.Columns.Contains(Att.Key))
                    {
                        //string Val = System.Web.HttpUtility.HtmlDecode(item[Att.InternalName].ToString());
                        //Remove ID prefix
                        //Val = Val.Replace(ID, "");

                        newDR[Att.Key] = GetFieldString(Att.Value);
                    }
                }


                Result.Rows.Add(newDR);
            }
            //Remove column prefix
            foreach (DataColumn item in Result.Columns)
                item.Caption = item.ColumnName = item.Caption.Replace("_x0020_", " ").Replace("_", "");
         



            return Result;
        }
        private string[] GetFields2010Properties()
        {
            string[] FieldsProperties = new string[] { 
            "CanBeDeleted",
            "DefaultValue",
            "Description",
            "Direction",
            "EnforceUniqueValues",
            "FieldTypeKind",
            "Filterable",
            "FromBaseType",
            "Group",
            "Hidden",
            "Id",
            "InternalName",
            "ObjectData",
            "ObjectVersion",
            "ReadOnlyField",
            "Required",
            "SchemaXml",
            "Scope",
            "Sealed",
            "ServerObjectIsNull",
            "Sortable",
            "StaticName",
            "Tag",
            "Title",
            "TypeAsString",
            "TypeDisplayName",
            "TypeShortDescription",
            "ValidationFormula",
            "ValidationMessage"
            };
            return FieldsProperties;
        }
        private DataTable XmlToDataTable2010(string ListID, ClientNS.FieldCollection ListFields)
        {

            DataTable Result = new DataTable("");
                Result.TableName = ListID;
            //Load field properties as columns
            foreach (string Property in GetFields2010Properties())
                Result.Columns.Add(Property);


            //Fill the fields properties data as rows
            foreach (ClientNS.Field field in ListFields)
            {
                DataRow newDR = Result.NewRow();

                foreach (PropertyInfo info in field.GetType().GetProperties())
                {
                    if (info.CanRead)
                    {
                        if (Result.Columns.Contains(info.Name))
                        {
                            object value = info.GetValue(field, null);
                            newDR[info.Name] = GetFieldString(value);
                        }

                    }
                }
                Result.Rows.Add(newDR);
            }

            return Result;
        }
        private DataTable XmlToDataTable2010(string ListID, XmlNodeList ListFields)
        {

            DataTable Result = new DataTable("");
            Result.TableName = ListID;
            //Load field properties as columns
            if (ListFields.Count <= 0) return Result;

            //Find the field with the most number of attributes
            XmlAttributeCollection AllAtts = ListFields[0].Attributes;
            for (int i = 0; i < ListFields.Count; i++)
            {
                if (ListFields[i].Attributes.Count > AllAtts.Count)
                    AllAtts = ListFields[i].Attributes;
            }
            foreach (XmlAttribute Property in AllAtts)
                Result.Columns.Add(Property.Name);

            //Add system column for consistancy with 2010 fields properties
            Result.Columns.Add("Title");
            Result.Columns.Add("CanBeDeleted");
            Result.Columns.Add("InternalName");


            //Fill the fields properties data as rows
            foreach (XmlNode field in ListFields)
            {
                DataRow newDR = Result.NewRow();
                foreach (XmlAttribute Att in field.Attributes)
                {

                    if (Result.Columns.Contains(Att.Name))
                    {
                        if (Att.Name == "DisplayName")
                        {
                            newDR["Title"] = field.Attributes[Att.Name].Value;
                        }
                        else if (Att.Name == "Name")
                        {
                            newDR["InternalName"] = field.Attributes[Att.Name].Value;
                        }

                        newDR[Att.Name] = field.Attributes[Att.Name].Value;
                    }
                }
                newDR["CanBeDeleted"] = "True";
                //Exclude virtual columns
                if(newDR["ID"] != null && newDR["ID"].ToString() !="")
                    Result.Rows.Add(newDR);
            }

            return Result;
        }
        private DataTable XmlWithNoAttributesToDataTable(XmlNode XmlData, string TableName)
        {
            DataTable Result = new DataTable(TableName);
            if (XmlData.ChildNodes == null || XmlData.ChildNodes.Count <= 0) return Result;
            //Create table columns
            foreach (XmlNode item in XmlData.ChildNodes[1].ChildNodes)
            {
                if (item.Name == "#whitespace") continue;
                Result.Columns.Add(item.Name);
            }

            foreach (XmlNode item in XmlData.ChildNodes)
            {
                if (item.Name == "#whitespace") continue;
                DataRow newDR = Result.NewRow();
                foreach (XmlNode listitem in item.ChildNodes)
                {
                    if (listitem.Name == "#whitespace") continue;
                    newDR[listitem.Name] = listitem.InnerText;
                }
                Result.Rows.Add(newDR);
            }
            return Result;
        }
        private DataTable XmlWithSchemaToDataTable(XmlNode XmlSchema, XmlNode XmlData, DataTable Result)
        {
            if (XmlData.ChildNodes == null || XmlData.ChildNodes.Count <= 0) return Result;
            //Create table columns
            foreach (XmlNode item in XmlSchema.ChildNodes[1].ChildNodes[1].ChildNodes[1].ChildNodes[1].ChildNodes[1].ChildNodes[1].ChildNodes)
            {
                if (item.Name == "#whitespace") continue;
                Result.Columns.Add(item.Attributes["name"].Value);
            }

            foreach (XmlNode item in XmlData.ChildNodes)
            {
                if (item.Name == "#whitespace") continue;
                DataRow newDR = Result.NewRow();
                foreach (XmlNode listitem in item.ChildNodes)
                {
                    if (listitem.Name == "#whitespace") continue;
                    newDR[listitem.Name] = listitem.InnerText;
                }
                Result.Rows.Add(newDR);
            }
            //Remove column prefix
            foreach (DataColumn item in Result.Columns)
                item.Caption = item.ColumnName = item.Caption.Replace("ows_", "").Replace("_x0020_", " ");



            return Result;
        }
        private DataTable XmlWithSchemaToDataTable(XmlNode XmlSchema, XmlNode XmlData, string TableName)
        {
            DataTable Result = new DataTable(TableName);
            if (XmlData.ChildNodes == null || XmlData.ChildNodes.Count <= 0) return Result;
            //Create table columns
            foreach (XmlNode item in XmlSchema.ChildNodes[1].ChildNodes[1].ChildNodes[1].ChildNodes[1].ChildNodes[1].ChildNodes[1].ChildNodes)
            {
                if (item.Name == "#whitespace") continue;
                Result.Columns.Add(item.Attributes["name"].Value);
            }

            foreach (XmlNode item in XmlData.ChildNodes)
            {
                if (item.Name == "#whitespace") continue;
                DataRow newDR = Result.NewRow();
                foreach (XmlNode listitem in item.ChildNodes)
                {
                    if (listitem.Name == "#whitespace") continue;
                    newDR[listitem.Name] = listitem.InnerText;
                }
                Result.Rows.Add(newDR);
            }
            return Result;
        }       
        private DataTable XmlNodeSingleToDataTable(XmlNode SingleXMLNode, string TableName)
        {
            DataTable Result = new DataTable(TableName);
            //Create table columns
            foreach (XmlAttribute Att in SingleXMLNode.Attributes)
                Result.Columns.Add(Att.Name);

                DataRow newDR = Result.NewRow();
                foreach (XmlAttribute Att in SingleXMLNode.Attributes)
                {
                    newDR[Att.Name] = Att.Value;
                }
                Result.Rows.Add(newDR);

                return Result;
        }
        private string GetServerUrlFromFullURL(string FullUrl)
        {
            int FirstSlashLocation = -1;
            if(FullUrl.Length>8) //Seach after 'http://' or 'https://'
            FirstSlashLocation = FullUrl.IndexOf('/', 8);
            if (FirstSlashLocation >= 0)
                return FullUrl.Substring(0, FirstSlashLocation) + "/";
            return FullUrl;
        }


        private XmlNodeList RunXPathQuery(XmlNode XmlNodeToQuery, string XPathQuery)
        {
            // load the complete XML node and all its child nodes into an
            // XML document
            XmlDocument Document = new XmlDocument();

            Document.LoadXml(XmlNodeToQuery.OuterXml);


            // all the possible namespaces used by SharePoint and a randomly
            // choosen prefix
            const string SharePointNamespacePrefix = "sp";
            const string SharePointNamespaceURI =
            "http://schemas.microsoft.com/sharepoint/soap/";
            const string ListItemsNamespacePrefix = "z";
            const string ListItemsNamespaceURI = "#RowsetSchema";
            const string PictureLibrariesNamespacePrefix = "y";
            const string PictureLibrariesNamespaceURI =
            "http://schemas.microsoft.com/sharepoint/soap/ois/";
            const string WebPartsNamespacePrefix = "w";
            const string WebPartsNamespaceURI =
            "http://schemas.microsoft.com/WebPart/v2";
            const string DirectoryNamespacePrefix = "d";
            const string DirectoryNamespaceURI =
            "http://schemas.microsoft.com/sharepoint/soap/directory/";
            const string DataRowSetNameSpacePrefix = "rs";
            const string DataRowSetNameSpaceURI = "urn:schemas-microsoft- com:rowset";

            // now associate with the xmlns namespaces (part of all XML
            // nodes returned from SharePoint), a namespace prefix that
            // we then can use in the queries
            XmlNamespaceManager NamespaceMngr =
            new XmlNamespaceManager(Document.NameTable);
            NamespaceMngr.AddNamespace(SharePointNamespacePrefix,
            SharePointNamespaceURI);
            NamespaceMngr.AddNamespace(ListItemsNamespacePrefix,
            ListItemsNamespaceURI);
            NamespaceMngr.AddNamespace(PictureLibrariesNamespacePrefix,
            PictureLibrariesNamespaceURI);
            NamespaceMngr.AddNamespace(WebPartsNamespacePrefix,
            WebPartsNamespaceURI);
            NamespaceMngr.AddNamespace(DirectoryNamespacePrefix,
            DirectoryNamespaceURI);
            NamespaceMngr.AddNamespace(DataRowSetNameSpacePrefix, DataRowSetNameSpaceURI);

            // run the XPath query and return the result nodes
            return Document.SelectNodes(XPathQuery, NamespaceMngr);
        }



        #endregion


        #region List Management
        /// <summary>
        /// Get all the lists within the current web
        /// </summary>
        /// <returns>Returns a datatable that contains all the lists with their properties</returns>
        public DataTable GetLists()
        {
            //Check if Lists webservice is ready
            if (!_WSReady)
                Init_WS();

            //Get all lists colelction
            XmlNode ListsNodes = _ListsWS.GetListCollection();

            //Prepare and fill the result collection
            DataTable Result = null;
            if (ListsNodes != null)
            {

                Result = XmlToDataTable(ListsNodes, "Lists");
            }
            return Result;
        }
        public string UpdateList(string ListID, string ListVersion, string FieldName)
        {

            XmlDocument xmlDoc = new System.Xml.XmlDocument();

            XmlNode ndDeleteFields = xmlDoc.CreateNode(XmlNodeType.Element,
                "Fields", "");
            XmlNode ndProperties = xmlDoc.CreateNode(XmlNodeType.Element, "List",
                "");
            XmlAttribute ndTitleAttrib =
                (XmlAttribute)xmlDoc.CreateNode(XmlNodeType.Attribute,
                "Title", "");
            XmlAttribute ndDescriptionAttrib =
                (XmlAttribute)xmlDoc.CreateNode(XmlNodeType.Attribute,
                "Description", "");
            XmlNode ndNewFields = xmlDoc.CreateNode(XmlNodeType.Element,
                "Fields", "");
            XmlNode ndUpdateFields = xmlDoc.CreateNode(XmlNodeType.Element,
                "Fields", "");

            ndTitleAttrib.Value = "List_Name";
            ndDescriptionAttrib.Value = "New_Description";

            ndProperties.Attributes.Append(ndTitleAttrib);
            ndProperties.Attributes.Append(ndDescriptionAttrib);

            ndDeleteFields.InnerXml = "<Method ID='5'>" +
               "<Field Name='Field1'/></Method>" +
               "<Method ID='6'><Field Name='Field2'/>" +
               "</Method>";

            ndNewFields.InnerXml = "<Method ID='1'>" +
               "<Field Type='DateTime' DateOnly='TRUE' DisplayName='NewField1' FromBaseType='TRUE'/>" +
               "</Method><Method ID='2'>" +
               "<Field Type='Text' DisplayName='NewField2' Required='TRUE' FromBaseType='TRUE' Description='Description'/>" +
               "</Method>";

            ndUpdateFields.InnerXml = "<Method ID='3'>" +
               "<Field Type='Number' Name='Field1' DisplayName='Field1_Display' Required='TRUE' FromBaseType='TRUE' Description='Description'/>" +
               "</Method><Method ID='4'>" +
               "<Field Type='Text' Name='Field2' DisplayName='Field2_Display' Required='TRUE' FromBaseType='TRUE' Description='Description'/>" +
               "</Method>";

            try
            {
                XmlNode ndReturn =
                   _ListsWS.UpdateList(ListID,
                   ndProperties, ndNewFields, ndUpdateFields, ndDeleteFields,
                   ListVersion);

                //MessageBox.Show(ndReturn.OuterXml);
            }

            catch (Exception ex)
            {
                //MessageBox.Show("Message:\n" + ex.Message + "\nStackTrace:\n" + 
                //   ex.StackTrace);
            }
            return "";
        }
        public string DeleteListFields(string ListID, string ListVersion, List<string> FieldNames)
        {
            if (FieldNames.Count <= 0)
                return "Operation Faild! Please supply the field(s) names to be deleted!";
            XmlDocument xmlDoc = new System.Xml.XmlDocument();

            XmlNode ndDeleteFields = xmlDoc.CreateNode(XmlNodeType.Element,
                "Fields", "");
            string DeleteMethodStubWithName = "<Method ID='{0}'>" +
               "<Field Name='{1}'/></Method>";
            for(int i=0; i< FieldNames.Count;i++)
            {
                ndDeleteFields.InnerXml += string.Format(DeleteMethodStubWithName, i+1, FieldNames[i]);
            }

            try
            {
                XmlNode ndReturn =
                   _ListsWS.UpdateList(ListID,
                   null, null, null, ndDeleteFields,
                   ListVersion);
            }

            catch (Exception ex)
            {
                return "Error";
            }
            return "Success";
        }
        private string AddNewListItems(string ListID, string ListVersion,string ViewGuid,Dictionary<string,string>[] ListItemsData)
        {
            XmlDocument doc = new XmlDocument();
            XmlElement batch = doc.CreateElement("Batch");

            batch.SetAttribute("OnError", "Continue");
            batch.SetAttribute("ListVersion", ListVersion);
            if(ViewGuid.Contains("{"))
                batch.SetAttribute("ViewName", ViewGuid);
            else
                batch.SetAttribute("ViewName", "{"+ ViewGuid + "}");
            string NewItemTemplate = "<Method ID='{0}' Cmd='New'>" +
               "<Field Name='ID'>New</Field>" +
               "<Field Name='Title'>{1}</Field>" +
               "{2}</Method>";
            string NewFieldDataTemplate = "<Field Name='{0}'>{1}</Field>";
            StringBuilder strBatch = new StringBuilder();
            int Counter = 1;
            foreach (Dictionary<string,string> ListItem in ListItemsData)
            {
                StringBuilder itemDataStr = new StringBuilder();
                string ItemTitle = "UnKnown";
                if (ListItem.Keys.Contains("Title"))
                    ItemTitle = ListItem["Title"];
                
                foreach (var itemData in ListItem)
                {
                    itemDataStr.AppendFormat(NewFieldDataTemplate, itemData.Key, itemData.Value);
                }
                strBatch.AppendFormat(NewItemTemplate, Counter++, ItemTitle, itemDataStr.ToString());
            }
            batch.InnerXml = strBatch.ToString();
            try
            {
                XmlNode Result = null;
                if (ListID.Contains("{"))
                    Result = _ListsWS.UpdateListItems(ListID, batch);
                else
                    Result = _ListsWS.UpdateListItems("{" + ListID + "}", batch);
            }
            catch (Exception ex)
            {
                return "Error";
            }
            return "Success";

        }
        private string UpdateListItems(string ListID, string ListVersion, string ViewGuid, Dictionary<string, string>[] ListItemsData)
        {
            XmlDocument doc = new XmlDocument();
            XmlElement batch = doc.CreateElement("Batch");

            batch.SetAttribute("OnError", "Continue");
            batch.SetAttribute("ListVersion", ListVersion);
            if (ViewGuid.Contains("{"))
                batch.SetAttribute("ViewName", ViewGuid);
            else
                batch.SetAttribute("ViewName", "{" + ViewGuid + "}");
            string NewItemTemplate = "<Method ID='{0}' Cmd='Update'>" +
               "<Field Name='ID'>{1}</Field>" +
               "{2}</Method>";
            string NewFieldDataTemplate = "<Field Name='{0}'>{1}</Field>";
            StringBuilder strBatch = new StringBuilder();
            int Counter = 1;
            foreach (Dictionary<string, string> ListItem in ListItemsData)
            {
                StringBuilder itemDataStr = new StringBuilder();
                string ItemID = "-1";
                if (ListItem.Keys.Contains("ID"))
                    ItemID = ListItem["ID"];
                else
                    throw new Exception("You have to add 'ID' field to the dictionary with the appropriate value");

                foreach (var itemData in ListItem)
                {
                    itemDataStr.AppendFormat(NewFieldDataTemplate, itemData.Key, itemData.Value);
                }
                strBatch.AppendFormat(NewItemTemplate, Counter++, ItemID, itemDataStr.ToString());
            }
            batch.InnerXml = strBatch.ToString();
            try
            {
                XmlNode Result = null;
                if (ListID.Contains("{"))
                    Result = _ListsWS.UpdateListItems(ListID, batch);
                else
                    Result = _ListsWS.UpdateListItems("{" + ListID + "}", batch);
            }
            catch (Exception ex)
            {
                return "Error";
            }
            return "Success";

        }
        private string DeleteListItems(string ListID, string ListVersion, string ViewGuid, List<string> ItemIDs)
        {
            XmlDocument doc = new XmlDocument();
            XmlElement batch = doc.CreateElement("Batch");

            batch.SetAttribute("OnError", "Continue");
            batch.SetAttribute("ListVersion", ListVersion);
            if (ViewGuid.Contains("{"))
                batch.SetAttribute("ViewName", ViewGuid);
            else
                batch.SetAttribute("ViewName", "{" + ViewGuid + "}");
            string DeleteItemTemplate = "<Method ID='{0}' Cmd='Delete'>" +
               "<Field Name='ID'>{1}</Field>" +
               "</Method>";

            StringBuilder strBatch = new StringBuilder();
            int Counter = 1;
            foreach (string itemID in ItemIDs)
            {
                strBatch.AppendFormat(DeleteItemTemplate,Counter++, itemID);
            }
            batch.InnerXml = strBatch.ToString();
            try
            {
                XmlNode Result = null;
                if (ListID.Contains("{"))
                    Result = _ListsWS.UpdateListItems(ListID, batch);
                else
                    Result = _ListsWS.UpdateListItems("{" + ListID + "}", batch);
            }
            catch (Exception ex)
            {
                return "Error";
            }
            return "Success";

        }

        private int GetListItemsCount(XmlNode response)
        {

            // load the response into an xml document
            XmlDocument xDoc = new XmlDocument();
            xDoc.LoadXml(response.OuterXml);

            // create a namespace manager
            XmlNamespaceManager ns = new XmlNamespaceManager(xDoc.NameTable);

            // add all the special SharePoint Namespaces in
            ns.AddNamespace("rs", "urn:schemas-microsoft-com:rowset");
            ns.AddNamespace("z", "#RowsetSchema");
            ns.AddNamespace("sp", "http://schemas.microsoft.com/sharepoint/soap/");
            ns.AddNamespace("s", "uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882");
            ns.AddNamespace("dt", "uuid:C2F41010-65B3-11d1-A29F-00AA00C14882");

            XPathNavigator nav = xDoc.CreateNavigator();

            string CountStr = xDoc.SelectSingleNode("/sp:listitems/rs:data/@ItemCount", ns).Value;
            int Count = -1;

            int.TryParse(CountStr, out Count);

            return Count;



        }
        public int GetListItemsSpecial()
        {
            XmlDocument xmlDoc = new System.Xml.XmlDocument();

            XmlNode ndQuery = xmlDoc.CreateNode(XmlNodeType.Element, "Query", "");
            XmlNode ndViewFields =
                xmlDoc.CreateNode(XmlNodeType.Element, "ViewFields", "");
            XmlNode ndQueryOptions =
                xmlDoc.CreateNode(XmlNodeType.Element, "QueryOptions", "");
            
            ndQueryOptions.InnerXml =
  "<Paging ListItemCollectionPositionNext=\"Paged%3DTRUE%26p_FSObjType%3D0%26p_ID%3D1651%26RootFolder%3D%252f\" />";

            //ndViewFields.InnerXml = "<FieldRef Name='File_x0020_Type' />";
            ndQuery.InnerXml = "<Where><Eq><FieldRef Name='UniqueId'/>" +
             "<Value Type='LookUp'>7cd7fc51-1ad0-4ce8-877d-d5042df449ff</Value></Eq></Where>";
            try
            {
                XmlNode ItemsNodes =
                    _ListsWS.GetListItems("270c0257-dae5-4390-8385-e83f4f6868d9", null, ndQuery,
                    ndViewFields, int.MaxValue.ToString(), ndQueryOptions, null);
                return GetListItemsCount(ItemsNodes);
            }

            catch (System.Web.Services.Protocols.SoapException)
            {
                //TODO: Add proper Error handeling
                throw;
            }
        }
        public int GetListItemsCount(string UniqueIdentifier)
        {
            XmlDocument xmlDoc = new System.Xml.XmlDocument();

            XmlNode ndQuery = xmlDoc.CreateNode(XmlNodeType.Element, "Query", "");
            /*
            ndQuery.InnerXml = "<Where><And><Gt><FieldRef Name=\"ID\"/>" +
                                "<Value Type=\"Counter\">0</Value></Gt>" +
                                "<Lt><FieldRef Name=\"ID\"/>" +
                                "<Value Type=\"Counter\">2876</Value></Lt></And></Where>";
             */
            XmlNode ndViewFields =
                xmlDoc.CreateNode(XmlNodeType.Element, "ViewFields", "");
            ndViewFields.InnerXml = "<FieldRef Name=\"ID\" />";
            XmlNode ndQueryOptions =
                xmlDoc.CreateNode(XmlNodeType.Element, "QueryOptions", "");

            ndQueryOptions.InnerXml =
                "<IncludeMandatoryColumns>False</IncludeMandatoryColumns>" +
                "<ViewAttributes Scope=\"Recursive\" />";

            try
            {
                XmlNode ItemsNodes =
                    _ListsWS.GetListItems(UniqueIdentifier, null, ndQuery,
                    ndViewFields, int.MaxValue.ToString(), ndQueryOptions, null);
                return GetListItemsCount(ItemsNodes);
            }

            catch (System.Web.Services.Protocols.SoapException)
            {
                //TODO: Add proper Error handeling
                throw;
            }
        }
        public DataTable GetListFieldsData(string UniqueIdentifier)
        {
            if (_UIVersion <= 3.0m)
                return GetListFieldsData2007(UniqueIdentifier);
            else
                return GetListFieldsData2010(UniqueIdentifier);

        }

        private DataTable GetListFieldsData2010(string UniqueIdentifier)
        {
            Web CurrentWeb = _Ctx.Web;
            _Ctx.Load(CurrentWeb);
            _Ctx.ExecuteQuery();
            List CurrentList = CurrentWeb.Lists.GetById(new Guid(UniqueIdentifier));
            _Ctx.Load(CurrentList);
            _Ctx.Load(CurrentList.Fields);
            _Ctx.ExecuteQuery();
            return XmlToDataTable2010(UniqueIdentifier, CurrentList.Fields);
        }

        private DataTable GetListFieldsData2007(string UniqueIdentifier)
        {

            XmlNode list = _ListsWS.GetList(UniqueIdentifier);
            //here “//sp:Field” is XPath query we need to pass.
            XmlNodeList nodes = RunXPathQuery(list, "//sp:Field");
            return XmlToDataTable2010(UniqueIdentifier, nodes);
            /*
            WSDspSts.QueryRequest myRequest = new WSDspSts.QueryRequest();

            WSDspSts.DSQuery sQuery = new WSDspSts.DSQuery();
            sQuery.select = string.Format("/list[@id='{0}']", UniqueIdentifier);

            myRequest.dsQuery = sQuery;

            WSDspSts.DspQuery spQuery = new WSDspSts.DspQuery();
            XmlDocument xmlDoc = new System.Xml.XmlDocument();
            XmlElement ndQuery = xmlDoc.CreateElement("And");

            //Using a filter that will return no items
            ndQuery.InnerXml = string.Format("<Gt><FieldRef Name=\"ID\"/>" +
            "<Value Type=\"Counter\">{0}</Value></Gt>" +
            "<Lt><FieldRef Name=\"ID\"/>" +
            "<Value Type=\"Counter\">{1}</Value></Lt>", 0, 0);

            spQuery.Where = ndQuery;
            myRequest.dsQuery.Query = spQuery;
            XmlNode ItemsNodes = _ListDataWS.Query(myRequest);
            XmlNode ItemsRows = ItemsNodes.ChildNodes[3];
            XmlNode ItemsSchema = ItemsNodes.ChildNodes[1];
            DataTable Results = new DataTable("ListItems");
            Results.Columns.Add("  ", typeof(Image));
            return XmlWithSchemaToDataTable(ItemsSchema, ItemsRows, Results);
             */
        }
        private DataTable GetListData2010(string UniqueIdentifier)
        {   
            //List CurrentList = _Ctx.Web.Lists.GetById(new Guid(UniqueIdentifier));
            Web CurrentWeb = _Ctx.Web;

            _Ctx.Load(CurrentWeb);
            //_Ctx.Load(_Ctx.Web, web => web.Lists.Include(l => l.Fields).Where(l => l.Id == new Guid(UniqueIdentifier))); ;
            //_Ctx.Load(CurrentList);
            _Ctx.ExecuteQuery();
            List CurrentList = CurrentWeb.Lists.GetById(new Guid(UniqueIdentifier));
            _Ctx.Load(CurrentList);
            _Ctx.Load(CurrentList.Fields);
            _Ctx.ExecuteQuery();
            CamlQuery Query = new CamlQuery();
            ListItemCollection ListItems = CurrentList.GetItems(Query);
            _Ctx.Load(ListItems);
            _Ctx.ExecuteQuery();

            return XmlToDataTable2010(CurrentList, ListItems);
            #region Old code using web services
            /*
            XmlDocument xmlDoc = new System.Xml.XmlDocument();

            XmlNode ndQuery = xmlDoc.CreateNode(XmlNodeType.Element, "Query", "");
            XmlNode ndViewFields =
                xmlDoc.CreateNode(XmlNodeType.Element, "ViewFields", "");
            XmlNode ndQueryOptions =
                xmlDoc.CreateNode(XmlNodeType.Element, "QueryOptions", "");

            ndQueryOptions.InnerXml =
                "<IncludeMandatoryColumns>TRUE</IncludeMandatoryColumns>" +
                "<DateInUtc>TRUE</DateInUtc>" +
                "<ViewFieldsOnly>FALSE</ViewFieldsOnly>";
            //ndViewFields.InnerXml = "<FieldRef Name='File_x0020_Type' />";
            //ndQuery.InnerXml = "<Where><And><Gt><FieldRef Name='Field1'/>" +
            // "<Value Type='Number'>5000</Value></Gt><Gt><FieldRef Name='Field2'/>" +
            // "<Value Type='DateTime'>2003-07-03T00:00:00</Value></Gt></And></Where>";
             
            try
            {
                XmlNode ItemsNodes =
                    _ListsWS.GetListItems(UniqueIdentifier, null, ndQuery,
                    ndViewFields, null, ndQueryOptions, null);
                return XmlToDataTable2010(ItemsNodes.ChildNodes[1], "Items");
                return null;
            }

            catch (System.Web.Services.Protocols.SoapException)
            {
                //TODO: Add proper Error handeling
                throw;
            }*/
            #endregion
            return null;
        }
        public DataTable GetListData(string ListUniqueIdentifier)
        {
            //Check the version of SharePoint
            if (UIVersion > 3.0m)
                return GetListData2010(ListUniqueIdentifier);
            if (string.IsNullOrEmpty(ListUniqueIdentifier)) return null;
            //TODO: Remove
            //int ItemsCount = GetListItemsCount(ListUniqueIdentifier);
            ///////////////////////
            WSDspSts.QueryRequest myRequest = new WSDspSts.QueryRequest();

            WSDspSts.DSQuery sQuery = new WSDspSts.DSQuery();
            sQuery.select = string.Format("/list[@id='{0}']", ListUniqueIdentifier);

            myRequest.dsQuery = sQuery;

            WSDspSts.DspQuery spQuery = new WSDspSts.DspQuery();
            XmlDocument xmlDoc = new System.Xml.XmlDocument();
            XmlElement ndQuery = xmlDoc.CreateElement("And");

            myRequest.dsQuery.Query = spQuery;
            XmlNode ItemsNodes = _ListDataWS.Query(myRequest);
            XmlNode ItemsRows = ItemsNodes.ChildNodes[3];
            XmlNode ItemsSchema = ItemsNodes.ChildNodes[1];
            DataTable Results = new DataTable("ListItems");
            Results.Columns.Add("  ", typeof(Image));
            return XmlWithSchemaToDataTable(ItemsSchema, ItemsRows, Results);
        }
        public DataTable GetListData(string ListUniqueIdentifier,string FromID,string ToID)
        {
            //Check the version of SharePoint
            if (UIVersion > 3.0m)
                return GetListData2010(ListUniqueIdentifier);
            if (string.IsNullOrEmpty(ListUniqueIdentifier)) return null;
            WSDspSts.QueryRequest myRequest = new WSDspSts.QueryRequest();

            WSDspSts.DSQuery sQuery = new WSDspSts.DSQuery();
            sQuery.select = string.Format("/list[@id='{0}']", ListUniqueIdentifier);

            myRequest.dsQuery = sQuery;

            WSDspSts.DspQuery spQuery = new WSDspSts.DspQuery();
            XmlDocument xmlDoc = new System.Xml.XmlDocument();
            XmlElement ndQuery = xmlDoc.CreateElement("And");
            ndQuery.InnerXml = string.Format("<Gt><FieldRef Name=\"ID\"/>" +
            "<Value Type=\"Counter\">{0}</Value></Gt>" +
            "<Lt><FieldRef Name=\"ID\"/>" +
            "<Value Type=\"Counter\">{1}</Value></Lt>",FromID ,ToID);

            spQuery.Where = ndQuery;
            myRequest.dsQuery.Query = spQuery; 
            XmlNode ItemsNodes = _ListDataWS.Query(myRequest);
            XmlNode ItemsRows = ItemsNodes.ChildNodes[3];
            XmlNode ItemsSchema = ItemsNodes.ChildNodes[1];
            DataTable Results = new DataTable("ListItems");
            Results.Columns.Add("  ", typeof(Image));
            return XmlWithSchemaToDataTable(ItemsSchema, ItemsRows, Results);
        }
        #endregion
        #region FileManagement
        public byte[] DownloadFile(string URL)
        {
            if (string.IsNullOrEmpty(URL)) return null;

            //Source and Destination Document URLs
            string sourceUrl = GetServerUrlFromFullURL(_WebUrl) + URL;
            //string sourceUrl = "http://mltech.us.ml.com" + URL;
            //Variables for Reading metadata’s of a document
            FieldInformation fieldInfo = new FieldInformation();
            FieldInformation[] fieldInfoArray = { fieldInfo };
            CopyResult cResult1 = new CopyResult();
            CopyResult cResult2 = new CopyResult();
            CopyResult[] cResultArray = { cResult1, cResult2 };

            //Receive a Document Contents  into Byte array (filecontents)
            byte[] fileContents = new Byte[4096];
            _CopyWS.GetItem(sourceUrl, out fieldInfoArray, out fileContents);
            return fileContents;
        }
        private bool DownloadFile(string URL, string ToFilePath)
        {
            byte[] fileContents = DownloadFile(URL);
            if (fileContents != null)
            {
                //Create a new file and write contents to that document
                FileStream fStream = new FileStream(ToFilePath, FileMode.Create, FileAccess.ReadWrite);
                fStream.Write(fileContents, 0, fileContents.Length);
                fStream.Close();
                return true;
            }
            return false;

        }
        #endregion
        #region SiteManagement
        public DataTable PropertiesToTableWithValues(object Obj, Type ObjType)
        {
            // get all public static properties of MyClass type
            PropertyInfo[] propertyInfos;
            propertyInfos = ObjType.GetProperties();
            // sort properties by name
            Array.Sort(propertyInfos,
                    delegate(PropertyInfo propertyInfo1, PropertyInfo propertyInfo2)
                    { return propertyInfo1.Name.CompareTo(propertyInfo2.Name); });

            DataTable Values = new DataTable("");
            // write property names
            Values.Rows.Add(new object[]{});
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                string PropName = propertyInfo.Name;
                Values.Columns.Add(PropName);
                //TODO: Handle the scenario of indexed properties check: http://stackoverflow.com/questions/4268244/iterating-through-an-indexed-property-reflection
                Values.Rows[Values.Rows.Count - 1][PropName] = propertyInfo.GetValue(Obj, null);
            }
            return Values;
        }
        public DataTable PropertiesToTableWithValues(object Obj, Type ObjType, string PrimaryKey)
        {
            // get all public static properties of MyClass type
            PropertyInfo[] propertyInfos;
            propertyInfos = ObjType.GetProperties();
            // sort properties by name
            Array.Sort(propertyInfos,
                    delegate(PropertyInfo propertyInfo1, PropertyInfo propertyInfo2)
                    { return propertyInfo1.Name.CompareTo(propertyInfo2.Name); });

            DataTable Values = new DataTable("");
            // write property names
            Values.Rows.Add(new object[] { });
            DataColumn PK = null;
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                string PropName = propertyInfo.Name;
                
                if(PrimaryKey.ToLower() == PropName.ToLower())
                    PK = Values.Columns.Add(PropName);
                else
                    Values.Columns.Add(PropName);
                //TODO: Handle the scenario of indexed properties check: http://stackoverflow.com/questions/4268244/iterating-through-an-indexed-property-reflection
                Values.Rows[Values.Rows.Count - 1][PropName] = propertyInfo.GetValue(Obj, null);
            }
            if (PK != null)
                Values.PrimaryKey = new DataColumn[] { PK };
            return Values;
        }
        public DataTable PropertiesToTableWithValues(object Obj, Type ObjType, string PrimaryKey, string PrimaryKeyData)
        {
            // get all public static properties of MyClass type
            PropertyInfo[] propertyInfos;
            propertyInfos = ObjType.GetProperties();
            // sort properties by name
            Array.Sort(propertyInfos,
                    delegate(PropertyInfo propertyInfo1, PropertyInfo propertyInfo2)
                    { return propertyInfo1.Name.CompareTo(propertyInfo2.Name); });

            DataTable Values = new DataTable("");
            // write property names
            Values.Rows.Add(new object[] { });
            DataColumn PK = null;
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                string PropName = propertyInfo.Name;

                if (PrimaryKey.ToLower() == PropName.ToLower())
                    PK = Values.Columns.Add(PropName);
                else
                    Values.Columns.Add(PropName);
                //TODO: Handle the scenario of indexed properties check: http://stackoverflow.com/questions/4268244/iterating-through-an-indexed-property-reflection
                Values.Rows[Values.Rows.Count - 1][PropName] = propertyInfo.GetValue(Obj, null);
            }
            if (PK != null)
            {
                if (Values.Rows.Count > 0)
                    Values.Rows[0][PK] = PrimaryKeyData;
            }
            else
            {
                PK = Values.Columns.Add(PrimaryKey);
                if (Values.Rows.Count > 0)
                    Values.Rows[0][PK] = PrimaryKeyData;
            }
            Values.PrimaryKey = new DataColumn[] { PK };
            return Values;
        }
        public DataTable XmlToDataTable(string Xml,string NodesName)
        {
            DataTable Data = new DataTable(NodesName);
            XDocument doc = XDocument.Parse(Xml);
            //Add columns

            foreach (var Node in doc.Element(NodesName).Elements())
            {
                foreach (var Attribute in Node.Attributes())
                    Data.Columns.Add(Attribute.Name.LocalName);
                break;
            }
            //Fill up the table with Data
            foreach (var Node in doc.Element(NodesName).Elements())
            {
                DataRow NewRow = Data.NewRow();
                foreach (var Attribute in Node.Attributes())
                {
                    NewRow[Attribute.Name.LocalName] = Attribute.Value;
                }
                Data.Rows.Add(NewRow);
            }
            
            return Data;

        }
        public DataTable GetWebMetaData(string SiteUrl, out DataTable Permissions, out DataTable Groups, out DataTable Users, out DataTable SubSitesInfo, bool TopLevelSite)
        {
            _SiteDataWS.Url = SiteUrl + "/_vti_bin/SiteData.asmx";
            WSSiteData._sWebMetadata siteMData = null;
            WSSiteData._sWebWithTime[] siteTime = null;
            WSSiteData._sListWithTime[] lstMData = null;
            WSSiteData._sFPUrl[] urls = null;
            SubSitesInfo = new DataTable("SubSiteInfo");
            Permissions = new DataTable("Permissions");
            Groups = new DataTable("Groups");
            Users = new DataTable("Users");


            string strSGroups;
            string[] strSGrpUsrs;
            string[] strSGrpGrps;
            DataTable MetaData = new DataTable("MetaData");
            //MetaData.Columns.Add("");
            _SiteDataWS.GetWeb(out siteMData, out siteTime, out lstMData, out urls,
                out strSGroups, out strSGrpUsrs, out strSGrpGrps);
            
            //Fill the subsite info
            DataTable SubsiteinfoTemp = new DataTable();
            foreach (var subsiteinfo in siteTime)
            {
                SubsiteinfoTemp = PropertiesToTableWithValues(subsiteinfo, typeof(WSSiteData._sWebWithTime));
                SubSitesInfo.Merge(SubsiteinfoTemp, false, MissingSchemaAction.Add);
            }
            //Helps when table is merged
            SubSitesInfo.PrimaryKey = new DataColumn[] { SubSitesInfo.Columns["Url"] };

            //Parse Permissions
            Permissions = XmlToDataTable(strSGroups, "Roles");

            //Parse Users
            string UsersXml = "";
            foreach (var user in strSGrpUsrs)
            {
                //Remove empty entries
                if (user.ToLower().Contains("<user id="))
                {
                    UsersXml += user.Replace("<Users>", "").Replace("</Users>","");
                }
            }
            UsersXml = "<Users>" + UsersXml + "</Users>";
            Users = XmlToDataTable(UsersXml, "Users");

            //Parse Groups
            string GroupsXml = "";
            foreach (var group in strSGrpGrps)
            {
                //Remove empty entries
                if (group.ToLower().Contains("<group id="))
                {
                    GroupsXml += group.Replace("<Groups>", "").Replace("</Groups>", "");
                }
            }
            GroupsXml = "<Groups>" + GroupsXml + "</Groups>";
            Groups = XmlToDataTable(GroupsXml, "Groups");

            DataTable SiteMetaData = PropertiesToTableWithValues(siteMData, typeof(WSSiteData._sWebMetadata),"Url",SiteUrl);
            DateTime LastItemModifiedDate = DateTime.MinValue;
            //Get the last item modified date
            foreach (var list in lstMData)
            {
                if (LastItemModifiedDate <= list.LastModified)
                    LastItemModifiedDate = list.LastModified;
            }
            //Add the last item modified value in the last modified date and add the site modified date 
            if (SiteMetaData.Columns.Contains("LastModified") && SiteMetaData.Rows.Count == 1)
            {
                if (TopLevelSite)
                {
                SiteMetaData.Columns.Add("SiteLastModifiedDate");
                SiteMetaData.Rows[0]["SiteLastModifiedDate"] = SiteMetaData.Rows[0]["LastModified"].ToString();
                }
                
              SiteMetaData.Rows[0]["LastModified"] = LastItemModifiedDate;
                
            }
           
            return SiteMetaData;
        }
        public List<string> GetSubSiteUrls()
        {
            string TempStr1 = "";
            string TempStr2 = "";
            string[] Groups = null;
            _sSiteMetadata SiteMetaData = new _sSiteMetadata();
            _sWebWithTime[] WebWithTime = null;
            _SiteDataWS.GetSite(out SiteMetaData, out WebWithTime, out TempStr1, out TempStr2, out Groups);
            List<string> Result = new List<string>();
            foreach (_sWebWithTime site in WebWithTime)
            {
                Result.Add(site.Url);
            }
            return Result;
        }
        
        /// <summary>
        /// Get all sub webs with their titles
        /// </summary>
        /// <returns>Returns a table with two columns (Title,Url)</returns>
        public DataTable GetSubSites()
        {
            //Get all webs colelction
            XmlNode WebsNodes = _WebsWS.GetAllSubWebCollection();
             //Prepare and fill the result collection
            DataTable Result = null;
            if (WebsNodes != null)
                Result = XmlToDataTable(WebsNodes, "Sites");
            return Result;
        }
        /// <summary>
        /// Get all sub webs with their properties
        /// </summary>
        /// <returns>Returns a table with 5 columns (Title,URL,description,language & theme)</returns>
        public DataTable GetSubSitesWithProperties(string Url)
        {
            //Set the site url
            _WebsWS.Url= Url + "/_vti_bin/Webs.asmx";
            //Get all webs colelction
            XmlNode WebsNodes = _WebsWS.GetWebCollection();
            //Prepare and fill the result collection
            DataTable Result = null;
            if (WebsNodes != null)
            {
                Result = new DataTable("Sites");
                Result.Columns.Add("Title");
                DataColumn PK = Result.Columns.Add("Url");
                Result.Columns.Add("Description");
                Result.Columns.Add("Language");
                Result.Columns.Add("Theme");
                //Helps when we merge to tables 
                Result.PrimaryKey = new DataColumn[] { PK };
                foreach (XmlNode site in WebsNodes.ChildNodes)
                {
                    Result.Rows.Add(GetSiteProperties(site.Attributes["Url"].Value).Rows[0].ItemArray); 
                }
            }
            return Result;
        }
        public DataTable GetSiteProperties(string WebUrl)
        {
            XmlNode WebNode = _WebsWS.GetWeb(WebUrl);
            //Prepare and fill the result collection
            DataTable Result = null;
            if (WebNode != null)
            {
                Result = XmlNodeSingleToDataTable(WebNode, "Site");
                //If FarmId is found in the web proprties that means we are connected to SP2010 farm
                if (Result.Columns.Contains("FarmId"))
                {
                    Init_WS2010();
                    UIVersion = 4.0m;
                }
            }
            return Result;
        }
        public DataTable GetRootSiteWithProperties()
        {
            return GetSiteProperties(_WebUrl);
        }
        #endregion

        #region User Management
        private XmlNode GetUserInfo(string UserNTID)
        {

            XmlNode ndUser = null;
            try
            {
                ndUser = _UserGroup.GetUserInfo(UserNTID);
            }
            catch (Exception ex)
            {


            }
            if (ndUser.HasChildNodes)
                return ndUser.ChildNodes[0];
            return ndUser;
        }
        #endregion


    }


}
